/**
 * 
 */
package com.honghao.student.util;

import java.util.concurrent.ExecutionException;

import net.spy.memcached.MemcachedClient;
import net.spy.memcached.internal.OperationFuture;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
/**
 * @author honghao.li
 *
 */
public class MemcachedCache implements Cache{

    private static final Logger LOGGER = LoggerFactory
            .getLogger(MemcachedCache.class);
    
	/** symbole between key's attributes */
	private static final String SYMBOLE_FOR_KEY = "&";
	/** symbole between key and cache name */
	private static final String SYMBOLE_FOR_NAME = "&";
	/** null result */
	private static final String NULL_RESULT = "null";
	/** Cache name */
	private String name;
	/** Memcached client instance */
	private MemcachedClient client;
	/** Expiry time (Unit:seconds) */
	private int expiry;
	
	/**
	 * Default Constructor
	 */
	public MemcachedCache() {
		super();
	}

	/**
	 * @param name
	 *            cache name
	 * @param client
	 *            MemcachedClient
	 * @param expiry
	 *            expire time (ms)
	 */
	public MemcachedCache(String name, MemcachedClient client, int expiry) {
		super();
		this.name = name;
		this.client = client;
		this.expiry = expiry;
	}

	/**
	 * @return the client
	 */
	public MemcachedClient getClient() {
		return client;
	}

	/**
	 * @param client
	 *            the client to set
	 */
	public void setClient(MemcachedClient client) {
		this.client = client;
	}

	/**
	 * @return the expiry
	 */
	public int getExpiry() {
		return expiry;
	}

	/**
	 * @param expiry
	 *            the expiry to set
	 */
	public void setExpiry(int expiry) {
		this.expiry = expiry;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#getName()
	 */
	@Override
	public String getName() {
		return this.name;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#getNativeCache()
	 */
	@Override
	public Object getNativeCache() {
		return this.client;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#get(java.lang.Object)
	 */
	@Override
	public ValueWrapper get(Object key) {
	    LOGGER.info("memcached key"+key.toString());
		Object ret = client.get(generateKey(key));
		if (ret == null) {
			return null;
		} else if (NULL_RESULT.equals(ret)) {
			ret = null;
		}
		return new SimpleValueWrapper(ret);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#put(java.lang.Object,
	 * java.lang.Object)
	 */
	@Override
	public void put(Object key, Object value) {
		if (value == null) {
			value = NULL_RESULT;
		}
		OperationFuture<Boolean> operation = client.add(generateKey(key),
				this.expiry, value);
		try {
			operation.get();
		} catch (InterruptedException e) {
			operation.cancel();
		} catch (ExecutionException e) {
			operation.cancel();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#evict(java.lang.Object)
	 */
	@Override
	public void evict(Object key) {
		String keyStr = generateKey(key);
		OperationFuture<Boolean> operation = client.delete(keyStr);
		try {
			operation.get();
		} catch (InterruptedException e) {
			operation.cancel();
		} catch (ExecutionException e) {
			operation.cancel();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#clear()
	 */
	@Override
	public void clear() {
		OperationFuture<Boolean> operation = client.flush();
		try {
			operation.get();
		} catch (InterruptedException e) {
			operation.cancel();
		} catch (ExecutionException e) {
			operation.cancel();
		}
	}

	/**
	 * Return generated key. [Cache name] + [SYMBOLE_FOR_NAME] + [Object name]
	 * 
	 * @param obj
	 *            to make key
	 * @return key
	 */
	public String generateKey(Object obj) {
		if (obj == null) {
			return this.name + SYMBOLE_FOR_NAME;
		} else if (obj instanceof String) {
			return this.name + SYMBOLE_FOR_KEY
					+ StringUtils.replace((String) obj, " ", "_");
		} else {
			return this.name + SYMBOLE_FOR_KEY + obj.toString();
		}
	}
}
